package com.awesomego.earth;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

public class Circle3DTexture {
    private static final float ANGLE_SPAN = 5.0f;

    private float mRadius;

    private ShortBuffer mIndexbuffer;
    private FloatBuffer mTexcoordBuffer;
    private FloatBuffer mVertexBuffer;
    private FloatBuffer mNormalBuffer;
    private int[] textureId = new int[]{-1, -1};

    private int mIndexCount = 0;

    private int mProgram;
    private int mMVPMatrixHandle;
    private int mPositionHandler;
    private int mTexcoordHandler;
    private int mTexture0Handler;
    private int mTexture1Handler;
    private int mNormalHandler;
    private int mLightPositionHandler;
    private int mEyePositionHandler;
    private int mMMMatrixHandler;

    public float[] lightPosition = new float[]{-1, 0, 1};
    public float[] eyePosition = new float[]{0, 0, 30};

    public float[] scale = new float[]{1, 1, 1};
    public float[] position = new float[]{0, 0, 0};
    public float[] angle = new float[]{0, 0, 0};
    protected float[] M_Matrix = new float[16];

    public Circle3DTexture(float radius, int dayTexture, int nightTexture) {
        super();

        mRadius = radius;
        if (dayTexture > 0) {
            initTexture(dayTexture);
        }
        if (nightTexture > 0) {
            initTexture(nightTexture);
        }
        initData();

        setLightPosition(lightPosition);
        setEyePosition(eyePosition);

        createProgram("light.vshader", "light.fshader");
    }

    public void createProgram(String vPath, String fPath) {
        Resources resources = TSApplication.getInstance().getResources();
        String vertexShader = ShaderUtil.loadFromAssetsFile(vPath, resources);
        String fragmentShdaer = ShaderUtil.loadFromAssetsFile(fPath, resources);
        mProgram = ShaderUtil.createProgram(vertexShader, fragmentShdaer);

        mNormalHandler = GLES20.glGetAttribLocation(mProgram, "aNormal");
        mPositionHandler = GLES20.glGetAttribLocation(mProgram, "aPosition");
        mTexcoordHandler = GLES20.glGetAttribLocation(mProgram, "aTexCoord");

        mLightPositionHandler = GLES20.glGetUniformLocation(mProgram, "aLightPosition");
        mEyePositionHandler = GLES20.glGetUniformLocation(mProgram, "aEyePosition");
        mMMMatrixHandler = GLES20.glGetUniformLocation(mProgram, "uMMatrix");
        mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
        mTexture0Handler = GLES20.glGetUniformLocation(mProgram, "uTexture0");
        mTexture1Handler = GLES20.glGetUniformLocation(mProgram, "uTexture1");
    }

    protected void initData() {
        int laCount = (int) (180.0f / ANGLE_SPAN + 1);
        int loCount = (int) (360.0f / ANGLE_SPAN + 1);

        float[] vertexs = new float[laCount * loCount * 3];
        float[] texCoords = new float[laCount * loCount * 2];

        int texCoordsIndex = 0;
        int vertexsIndex = 0;

        float laaIndex = 0.0f;

        for (float la = -90; la <= 90; la += ANGLE_SPAN) {
            double laRadian = Math.toRadians(la);
            double laCos = Math.cos(laRadian);
            double laSin = Math.sin(laRadian);

            float looIndex = 0.0f;

            for (float lo = 0; lo <= 360; lo += ANGLE_SPAN) {
                double loRadian = Math.toRadians(lo);
                double loCos = Math.cos(loRadian);
                double loSin = Math.sin(loRadian);

                vertexs[vertexsIndex++] = (float) (mRadius * laCos * loCos);
                vertexs[vertexsIndex++] = (float) (mRadius * laSin);
                vertexs[vertexsIndex++] = (float) (mRadius * laCos * loSin);

                texCoords[texCoordsIndex++] = 1.0f - looIndex / (loCount - 1);
                texCoords[texCoordsIndex++] = 1.0f - laaIndex / (laCount - 1);

                looIndex++;
            }
            laaIndex++;
        }

        short[] indexes = new short[laCount * loCount * 6];
        int index = 0;
        for (int loIndex = 0; loIndex < loCount - 1; loIndex++) {
            short loo = (short) loIndex;
            short looNext = (short) (loIndex + 1);

            for (int laIndex = 0; laIndex < laCount - 1; laIndex++) {
                short laa = (short) (laIndex * loCount);
                short laaNext = (short) ((laIndex + 1) * loCount);

                indexes[index++] = (short) (laa + loo);
                indexes[index++] = (short) (laa + looNext);
                indexes[index++] = (short) (laaNext + looNext);

                indexes[index++] = (short) (laaNext + looNext);
                indexes[index++] = (short) (laaNext + loo);
                indexes[index++] = (short) (laa + loo);
                mIndexCount += 6;
            }
        }

        mVertexBuffer = BufferUtils.getBuffer(vertexs);
        mIndexbuffer = BufferUtils.getBuffer(indexes);
        mTexcoordBuffer = BufferUtils.getBuffer(texCoords);

        mVertexBuffer.position(0);

        mNormalBuffer = mVertexBuffer;
        mNormalBuffer.position(0);
    }

    public void visit() {
        GLES20.glUseProgram(mProgram);
        initM_Matrix();
        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false,
                MatrixUtils.getFinalMatrix(M_Matrix), 0);
        if (textureId[0] > -1) {
            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId[0]);
            GLES20.glUniform1i(mTexture0Handler, 0);
        }
        if (textureId[1] > -1) {
            GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId[1]);
            GLES20.glUniform1i(mTexture1Handler, 1);
        }

        GLES20.glVertexAttribPointer(mPositionHandler, 3,
                GLES20.GL_FLOAT, false, 3 * 4, mVertexBuffer);
        GLES20.glEnableVertexAttribArray(mPositionHandler);

        GLES20.glVertexAttribPointer(mTexcoordHandler, 2,
                GLES20.GL_FLOAT, false, 2 * 4, mTexcoordBuffer);
        GLES20.glEnableVertexAttribArray(mTexcoordHandler);

        GLES20.glVertexAttribPointer(mNormalHandler, 3, GLES20.GL_FLOAT,
                false, 3 * 4, mNormalBuffer);
        GLES20.glEnableVertexAttribArray(mNormalHandler);

        GLES20.glUniformMatrix4fv(mMMMatrixHandler, 1, false, M_Matrix, 0);

        GLES20.glUniform3f(mLightPositionHandler, lightPosition[0],
                lightPosition[1], lightPosition[2]);
        GLES20.glUniform3f(mEyePositionHandler, eyePosition[0],
                eyePosition[1], eyePosition[2]);

        GLES20.glDrawElements(GLES20.GL_TRIANGLES, mIndexCount,
                GLES20.GL_UNSIGNED_SHORT, mIndexbuffer);
    }

    private void initTexture(int id) {
        int texId = -1;
        if (textureId[0] == -1) {
            GLES20.glGenTextures(1, textureId, 0);
            texId = textureId[0];
        } else if (textureId[0] > -1) {
            GLES20.glGenTextures(1, textureId, 1);
            texId = textureId[1];
        }

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
                GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
                GLES20.GL_CLAMP_TO_EDGE);

        InputStream stream = null;
        Bitmap bitmap = null;

        try {
            stream = TSApplication.getInstance().getResources().openRawResource(id);
            bitmap = BitmapFactory.decodeStream(stream);
            GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
        } catch (Exception e) {
        } finally {
            try {
                if (bitmap != null) {
                    bitmap.recycle();
                }
                if (stream != null) {
                    stream.close();
                }
            } catch (IOException e) {
            }
        }
    }

    public final void setLightPosition(float[] lightPosition) {
        for (int i = 0; i < 3; i++) {
            this.lightPosition[i] = lightPosition[i];
        }
    }

    public final void setEyePosition(float[] eyePosition) {
        for (int i = 0; i < 3; i++) {
            this.eyePosition[i] = eyePosition[i];
        }
    }

    protected void initM_Matrix() {
        Matrix.setRotateM(M_Matrix, 0, 0, 0, 1, 0);
        Matrix.translateM(M_Matrix, 0, position[0], position[1], position[2]);
        Matrix.scaleM(M_Matrix, 0, scale[0], scale[1], scale[2]);
        Matrix.rotateM(M_Matrix, 0, angle[0], 1, 0, 0);
        Matrix.rotateM(M_Matrix, 0, angle[1], 0, 1, 0);
        Matrix.rotateM(M_Matrix, 0, angle[2], 0, 0, 1);
    }

    public void drawSelf() {
        visit();
    }
}